﻿
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using GDFlacTool.Common;

namespace GDFlacTool.Forms
{
    public partial class FormCheckTag : Form
    {
        private readonly IList<Track> _tracks;
        private const string _line = "---------------------------------------------------------------------------------";
        private readonly string lastInput = string.Empty;
        private int _errorCounter;
        private int _warningCounter;

        delegate void SetTextCallback(string text);
        
        public FormCheckTag(IList<Track> tracks)
        {
            InitializeComponent();

            _tracks = tracks;
        }

        private void FormCheckTag_Load(object sender, EventArgs e)
        {
            InitConsole();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            if(btnCancel.Text == "&Cancel")
            {
                backgroundWorker.CancelAsync();
            }
            else
            {
                Close();
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            _errorCounter = 0;
            _warningCounter = 0;

            if (richTextBoxConsole.Lines.Length != 2)

            ClearOutput();

            btnStart.Enabled = false;

            btnCancel.Text = "&Cancel";

            backgroundWorker.RunWorkerAsync();
        }

        #region Helper Methods.

        private void SetText(string text)
        {
            if (InvokeRequired)
            {
                var d = new SetTextCallback(SetText);
                Invoke(d, new object[] { text });
            }
            else
            {
                Text = text;
            }
        }

        #endregion

        #region Console Output Functions.

        public void WriteOutputLine(string output, Color color)
        {
            if (string.IsNullOrEmpty(lastInput) == false &&
                (output == lastInput || output.Replace("\r\n", "") == lastInput))
                return;

            Invoke((Action)(() =>
            {
                richTextBoxConsole.SelectionColor = color;
                richTextBoxConsole.SelectedText += output + "\r\n";
                richTextBoxConsole.ScrollToCaret();
                richTextBoxConsole.Update();
            }));

            Thread.Sleep(40);
        }

        public void WriteOutput(string output, Color color)
        {
            if (string.IsNullOrEmpty(lastInput) == false &&
                (output == lastInput || output.Replace("\r\n", "") == lastInput))
                return;
            Invoke((Action)(() =>
            {
                richTextBoxConsole.SelectionColor = color;
                richTextBoxConsole.Select(richTextBoxConsole.Text.Length, 0);
                richTextBoxConsole.SelectedText += output;
            }));
        }

        public void ClearOutput()
        {
            richTextBoxConsole.Clear();

            InitConsole();
        }

        public void InitConsole()
        {
            string initText1 = string.Format("GD Flac Tool [Version {0}]", Application.ProductVersion);
            string initText2 = string.Format("Click the start button to perform the tag check...");

            WriteOutputLine(initText1, Color.Aqua);
            WriteOutputLine(initText2, Color.Gainsboro);
            WriteOutputLine("", Color.Black);
        }

        #endregion

        #region Check Tag Functions.

        private void CheckFileForCorruptedTag(Track track)
        {
            string message;

            WriteOutput(string.Format("Testing file for corrupted tags... "), Color.Aqua);
            
            if(track.IsTagCorrupt(out message))
            {
                
                WriteOutputLine(string.Format("TEST FAILED: {0}", message), Color.Red);
                _errorCounter++;
            }
            else
            {
                WriteOutputLine(string.Format("TEST OK"), Color.Lime);
            }
        }

        private void CheckFileForMissingCoverArt(Track track)
        {
            bool foundError = false;

            WriteOutput(string.Format("Testing file for missing cover art... "), Color.Aqua);

            if(!track.HasFileCoverArt)
            {
                WriteOutputLine(string.Format("TEST FAILED: Embedded cover art is missing"),Color.Red);
                _errorCounter++;
                foundError = true;
            }
            else
            {
                WriteOutputLine(string.Format("TEST OK"), Color.Lime);
            }

            if(!foundError)
            {
                WriteOutput(string.Format("Testing size of cover art... "), Color.Aqua);

                if(track.ArtImage.Height >= 600 && track.ArtImage.Width >= 600)
                {
                    WriteOutputLine(string.Format("TEST OK"), Color.Lime);
                }
                else
                {
                    WriteOutputLine(string.Format("TEST GENERATED WARNING: Cover art image size is to small (Height={0}, Width={1})", track.ArtImage.Height, track.ArtImage.Width), Color.Yellow);
                    _warningCounter++;
                }
            }
        }

        private void CheckArtistTag(Track track)
        {
            WriteOutput(string.Format("Testing artist tag... "), Color.Aqua);

            if (string.IsNullOrEmpty(track.Artist))
            {
                WriteOutputLine(string.Format("TEST FAILED: The artist tag is missing!"), Color.Red);
                _errorCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }
        }

        private void CheckAlbumTag(Track track)
        {
            WriteOutput(string.Format("Testing album tag... "), Color.Aqua);

            if (string.IsNullOrEmpty(track.Album))
            {
                WriteOutputLine(string.Format("TEST FAILED: The album tag is missing!"), Color.Red);
                _errorCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }
        }

        private void CheckYearTag(Track track)
        {
            bool foundError = false;

            WriteOutput(string.Format("Testing year tag... "), Color.Aqua);

            if (string.IsNullOrEmpty(track.Year))
            {
                WriteOutputLine(string.Format("TEST FAILED: The year tag is missing!"), Color.Red);
                _errorCounter++;
                foundError = true;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }

            if(!foundError)
            {
                WriteOutput(string.Format("Testing format of year tag... "), Color.Aqua);

                if(track.Year.Length  != 4 || !Functions.IsNumeric(track.Year))
                {
                    WriteOutputLine(string.Format("TEST FAILED: The year tag is not numeric or not four digits!"), Color.Red);
                }
                else
                {
                    WriteOutputLine("TEST OK", Color.Lime);
                }
            }
        }

        private void CheckGenreTag(Track track)
        {
            WriteOutput(string.Format("Testing genre tag... "), Color.Aqua);

            if (string.IsNullOrEmpty(track.Genre))
            {
                WriteOutputLine(string.Format("TEST FAILED: The genre tag is missing!"), Color.Red);
                _errorCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }
        }

        private void CheckCatalogTag(Track track)
        {
            WriteOutput(string.Format("Testing catalog (SHNID) tag... "), Color.Aqua);

            if (string.IsNullOrEmpty(track.Catalog) && track.Artist.ToUpper() == "GRATEFUL DEAD")
            {
                WriteOutputLine(string.Format("TEST GENERATED WARNING: The catalog (SHNID) tag is missing!"), Color.Yellow);
                _warningCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }
        }

        private void CheckAlbumArtistTag(Track track)
        {
            bool foundError = false;

            WriteOutput(string.Format("Testing album artist tag... "), Color.Aqua);

            if (string.IsNullOrEmpty(track.Albumartist))
            {
                WriteOutputLine(string.Format("TEST FAILED: The album artist tag is missing!"), Color.Red);
                foundError = true;
                _errorCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }

            if(!foundError && !string.IsNullOrEmpty(track.Artist))
            {
                WriteOutput(string.Format("Testing album artist tag compared to artist tag... "), Color.Aqua);

                if (track.Artist != track.Albumartist)
                {
                    WriteOutputLine(string.Format("TEST GENERATED WARNING: The album artist tag is different from the artist tag!"), Color.Yellow);
                    _warningCounter++;
                }
                else
                {
                    WriteOutputLine("TEST OK", Color.Lime);
                }
            }
        }

        private void CheckTitleTag(Track track)
        {
            WriteOutput(string.Format("Testing title tag... "), Color.Aqua);

            if (string.IsNullOrEmpty(track.Albumartist))
            {
                WriteOutputLine(string.Format("TEST FAILED: The title tag is missing!"), Color.Red);
                _errorCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }
        }

        private void CheckTrackTag(Track track)
        {
            bool foundError = false;

            WriteOutput(string.Format("Testing track number tag... "), Color.Aqua);

            if (string.IsNullOrEmpty(track.TrackNumber))
            {
                WriteOutputLine(string.Format("TEST FAILED: The track number tag is missing!"), Color.Red);
                foundError = true;
                _errorCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }

            if(!foundError)
            {
                WriteOutput(string.Format("Testing track number tag length... "), Color.Aqua);

                string[] test = track.TrackNumber.Split('/');

                if(test.Length != 2)
                {
                    WriteOutputLine(string.Format("TEST FAILED: The track number tag is in wrong format, should be (xx/xx)"), Color.Red);
                    foundError = true;
                    _errorCounter++;
                }
                else
                {
                    WriteOutputLine("TEST OK", Color.Lime);
                }
            }

            if (!foundError)
            {
                WriteOutput(string.Format("Testing track number tag format... "), Color.Aqua);

                string[] test = track.TrackNumber.Split('/');

                string first = test[0];
                string second = test[1];

                if (first.Length != 2 || second.Length != 2)
                {
                    WriteOutputLine(string.Format("TEST FAILED: The track number tag is in wrong format, should be (xx/xx)"), Color.Red);
                    foundError = true;
                    _errorCounter++;
                }
                else
                {
                    WriteOutputLine("TEST OK", Color.Lime);
                }
            }

            if(!foundError)
            {
                WriteOutput(string.Format("Testing that track number tag is numeric... "), Color.Aqua);

                string[] test = track.TrackNumber.Split('/');

                if(!Functions.IsNumeric(test[0]) || !Functions.IsNumeric(test[1]))
                {
                    WriteOutputLine(string.Format("TEST FAILED: The track number tag is not numeric, should be (01/23)"), Color.Red);
                }
                else
                {
                    WriteOutputLine("TEST OK", Color.Lime);
                }
            }
        }

        private void CheckDiscTag(Track track)
        {
            bool foundError = false;

            WriteOutput(string.Format("Testing disc number tag... "), Color.Aqua);

            if (string.IsNullOrEmpty(track.DiscNumber))
            {
                WriteOutputLine(string.Format("TEST FAILED: The disc number tag is missing!"), Color.Red);
                foundError = true;
                _errorCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }

            if (!foundError)
            {
                WriteOutput(string.Format("Testing disc number tag length... "), Color.Aqua);

                string[] test = track.DiscNumber.Split('/');

                if (test.Length != 2)
                {
                    WriteOutputLine(string.Format("TEST FAILED: The disc number tag is in wrong format, should be (x/x)"), Color.Red);
                    foundError = true;
                    _errorCounter++;
                }
                else
                {
                    WriteOutputLine("TEST OK", Color.Lime);
                }
            }

            if (!foundError)
            {
                WriteOutput(string.Format("Testing disc number tag format... "), Color.Aqua);

                string[] test = track.DiscNumber.Split('/');

                string first = test[0];
                string second = test[1];

                if (first.Length != 1 || second.Length != 1)
                {
                    WriteOutputLine(string.Format("TEST FAILED: The disc number tag is in wrong format, should be (x/x)"), Color.Red);
                    foundError = true;
                    _errorCounter++;
                }
                else
                {
                    WriteOutputLine("TEST OK", Color.Lime);
                }
            }

            if (!foundError)
            {
                WriteOutput(string.Format("Testing that disc number tag is numeric... "), Color.Aqua);

                string[] test = track.DiscNumber.Split('/');

                if (!Functions.IsNumeric(test[0]) || !Functions.IsNumeric(test[1]))
                {
                    WriteOutputLine(string.Format("TEST FAILED: The disc number tag is not numeric, should be (1/3)"), Color.Red);
                }
                else
                {
                    WriteOutputLine("TEST OK", Color.Lime);
                }
            }
        }

        private void CheckBitrate(Track track)
        {
            WriteOutput(string.Format("Testing audio file bitrate... "), Color.Aqua);

            int bitrate;
            string trackBitrate = track.BitRate;

            if (trackBitrate.Contains(" (VBR)"))
                trackBitrate = trackBitrate.Replace(" (VBR)", "");
            if (trackBitrate.Contains(" (CBR)"))
                trackBitrate = trackBitrate.Replace(" (CBR)", "");

            int.TryParse(trackBitrate, out bitrate);

            if (bitrate < 320)
            {
                WriteOutputLine(string.Format("TEST GENERATED WARNING: The bitrate in the audio file is lower that 320!"), Color.Yellow);
                _warningCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }
        }

        private void CheckReleaseType(Track track)
        {
            WriteOutput(string.Format("Testing release type... "), Color.Aqua);

            int releastype;

            int.TryParse(track.ReleaseType, out releastype);

            if (releastype < 1 && releastype > 13)
            {
                WriteOutputLine(string.Format("TEST GENERATED WARNING: The release type is not set!"), Color.Yellow);
                _warningCounter++;
            }
            else
            {
                WriteOutputLine("TEST OK", Color.Lime);
            }
        }

        #endregion

        #region BackGroundWorker Events.

        private void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            int counter = 1;

            WriteOutputLine(string.Format("{0} {1} - Started tag check of {2} files:", DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString(), _tracks.Count), Color.Yellow);
            WriteOutputLine("", Color.Black);

            foreach (Track track in _tracks)
            {
                WriteOutputLine(string.Format("Checking file {0} of {1} - [{2}]", counter, _tracks.Count, track.FileName), Color.Gainsboro);

                //string percent = string.Format("{0} %", counter/_tracks.Count*100);
                string progress = string.Format("Checking file {0} of {1}", counter, _tracks.Count);

                SetText(string.Format("Check Tags - {0} [{1}]", progress, track.FileName));

                CheckFileForCorruptedTag(track);

                CheckFileForMissingCoverArt(track);

                CheckArtistTag(track);

                CheckAlbumTag(track);

                CheckYearTag(track);

                CheckGenreTag(track);

                CheckCatalogTag(track);

                CheckAlbumArtistTag(track);

                CheckTitleTag(track);

                CheckTrackTag(track);

                CheckDiscTag(track);

                CheckBitrate(track);

                CheckReleaseType(track);

                WriteOutputLine("", Color.Black);

                Thread.Sleep(75);

                if(backgroundWorker.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                counter++;
            }
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            WriteOutputLine(_line, Color.Silver);
            WriteOutputLine("", Color.Black);
            WriteOutputLine("Finished check:", Color.Gainsboro);

            if (_errorCounter > 0)
            {
                WriteOutputLine(string.Format("Found {0} errors", _errorCounter), Color.Red);

            }
            else
            {
                WriteOutputLine(string.Format("Found {0} errors", _errorCounter), Color.Lime);

            }

            if (_warningCounter > 0)
            {
                WriteOutputLine(string.Format("Found {0} warnings", _warningCounter), Color.Yellow);
            }
            else
            {
                WriteOutputLine(string.Format("Found {0} warnings", _warningCounter), Color.Lime);
            }

            Text = "Check Tags";
            btnStart.Enabled = true;
            btnCancel.Text = "&Close";
        }

        #endregion
    }
}
